/*-------------------------------------------------------------------------
 * relpath.c
 *		共享前端/后端代码以计算关系文件的路径名
 *
 * 此模块还包含与fork名称相关的一些逻辑。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/common/relpath.c
 *
 *-------------------------------------------------------------------------
 */
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include "catalog/pg_tablespace_d.h"
#include "common/relpath.h"
#include "storage/backendid.h"


/*
 * 按fork编号查找fork名称的查找表。
 *
 * 如果您添加了新的条目，请记得更新下面
 * forkname_to_number()中的错误提示，并更新
 * pg_relation_size()的SGML文档。
 */
const char *const forkNames[] = {
	"main",						/* MAIN_FORKNUM */
	"fsm",						/* FSM_FORKNUM */
	"vm",						/* VISIBILITYMAP_FORKNUM */
	"init"						/* INIT_FORKNUM */
};

StaticAssertDecl(lengthof(forkNames) == (MAX_FORKNUM + 1),
				 "array length mismatch");

/*
 * forkname_to_number - 按名称查找fork编号
 *
 * 在后端，如果没有匹配会抛出错误；在前端，我们只
 * 返回InvalidForkNumber。
 */
ForkNumber
forkname_to_number(const char *fc_forkName)
{
	ForkNumber	fc_forkNum;

	for (fc_forkNum = 0; fc_forkNum <= MAX_FORKNUM; fc_forkNum++)
		if (strcmp(forkNames[fc_forkNum], fc_forkName) == 0)
			return fc_forkNum;

#ifndef FRONTEND
	ereport(ERROR,
			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
			 errmsg("invalid fork name"),
			 errhint("Valid fork names are \"main\", \"fsm\", "
					 "\"vm\", and \"init\".")));
#endif

	return InvalidForkNumber;
}

/*
 * forkname_chars
 *		我们用它来判断文件名是否可以是一个关系
 *		fork（而不是以某种方式出现在数据库目录中的
 *		奇怪命名的孤立文件）。如果传入的字符串以
 *		一个fork名称（不是主fork名称）开头，我们返回
 *		它的长度，并将*fork（如果不为NULL）设置为
 *		fork编号。如果没有，我们返回0。
 *
 * 请注意，目前的编码假设不存在作为其他
 * fork名称前缀的fork名称。
 */
int forkname_chars(const char *fc_str, ForkNumber *fc_fork)
{
	ForkNumber	fc_forkNum;

	for (fc_forkNum = 1; fc_forkNum <= MAX_FORKNUM; fc_forkNum++)
	{
		int			fc_len = strlen(forkNames[fc_forkNum]);

		if (strncmp(forkNames[fc_forkNum], fc_str, fc_len) == 0)
		{
			if (fc_fork)
				*fc_fork = fc_forkNum;
			return fc_len;
		}
	}
	if (fc_fork)
		*fc_fork = InvalidForkNumber;
	return 0;
}


/*
 * GetDatabasePath - 构建到数据库目录的路径
 *
 * 结果是palloc分配的字符串。
 *
 * XXX这必须与GetRelationPath()一致！
 */
char * GetDatabasePath(Oid fc_dbNode, Oid fc_spcNode)
{
	if (fc_spcNode == GLOBALTABLESPACE_OID)
	{
		/* 共享系统关系位于{datadir}/global */
		Assert(fc_dbNode == 0);
		return pstrdup("global");
	}
	else if (fc_spcNode == DEFAULTTABLESPACE_OID)
	{
		/* 默认表空间为{datadir}/base */
		return psprintf("base/%u", fc_dbNode);
	}
	else
	{
		/* 所有其他表空间通过符号链接访问 */
		return psprintf("pg_tblspc/%u/%s/%u",
						fc_spcNode, TABLESPACE_VERSION_DIRECTORY, fc_dbNode);
	}
}

/*
 * GetRelationPath - 构建到关系文件的路径
 *
 * 结果是palloc分配的字符串。
 *
 * 注意：理想情况下，backendId应声明为BackendId类型，
 * 但是relpath.h必须包含仅限后端的头文件来实现；
 * 考虑到BackendId只是整数，这似乎不值得费心。
 */
char * GetRelationPath(Oid fc_dbNode, Oid fc_spcNode, Oid fc_relNode,
				int fc_backendId, ForkNumber fc_forkNumber)
{
	char	   *fc_path;

	if (fc_spcNode == GLOBALTABLESPACE_OID)
	{
		/* 共享系统关系位于{datadir}/global */
		Assert(fc_dbNode == 0);
		Assert(fc_backendId == InvalidBackendId);
		if (fc_forkNumber != MAIN_FORKNUM)
			fc_path = psprintf("global/%u_%s",
							fc_relNode, forkNames[fc_forkNumber]);
		else
			fc_path = psprintf("global/%u", fc_relNode);
	}
	else if (fc_spcNode == DEFAULTTABLESPACE_OID)
	{
		/* 默认表空间为{datadir}/base */
		if (fc_backendId == InvalidBackendId)
		{
			if (fc_forkNumber != MAIN_FORKNUM)
				fc_path = psprintf("base/%u/%u_%s",
								fc_dbNode, fc_relNode,
								forkNames[fc_forkNumber]);
			else
				fc_path = psprintf("base/%u/%u",
								fc_dbNode, fc_relNode);
		}
		else
		{
			if (fc_forkNumber != MAIN_FORKNUM)
				fc_path = psprintf("base/%u/t%d_%u_%s",
								fc_dbNode, fc_backendId, fc_relNode,
								forkNames[fc_forkNumber]);
			else
				fc_path = psprintf("base/%u/t%d_%u",
								fc_dbNode, fc_backendId, fc_relNode);
		}
	}
	else
	{
		/* 所有其他表空间通过符号链接访问 */
		if (fc_backendId == InvalidBackendId)
		{
			if (fc_forkNumber != MAIN_FORKNUM)
				fc_path = psprintf("pg_tblspc/%u/%s/%u/%u_%s",
								fc_spcNode, TABLESPACE_VERSION_DIRECTORY,
								fc_dbNode, fc_relNode,
								forkNames[fc_forkNumber]);
			else
				fc_path = psprintf("pg_tblspc/%u/%s/%u/%u",
								fc_spcNode, TABLESPACE_VERSION_DIRECTORY,
								fc_dbNode, fc_relNode);
		}
		else
		{
			if (fc_forkNumber != MAIN_FORKNUM)
				fc_path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u_%s",
								fc_spcNode, TABLESPACE_VERSION_DIRECTORY,
								fc_dbNode, fc_backendId, fc_relNode,
								forkNames[fc_forkNumber]);
			else
				fc_path = psprintf("pg_tblspc/%u/%s/%u/t%d_%u",
								fc_spcNode, TABLESPACE_VERSION_DIRECTORY,
								fc_dbNode, fc_backendId, fc_relNode);
		}
	}
	return fc_path;
}
